/*
 * Command.cpp
 *
 *  Created on: May 12, 2011
 *      Author: Stijn, Bart, Dave
 */

#include "BuiltInCommand.h"

#include "Command.h"

Command::Command(std::string name, std::vector<std::string>* pathVariables) {
	this->name = name;
	this->pathVariables = pathVariables;
	arguments = new std::vector<std::string>();
	childPid = 0;
	isBackground = false;
	priorityInc = 0;

	ioInput = "";
	ioOutput = "";
	append = false;

	expressions[C_CD] = "cd";
	expressions[C_CHDIR] = "chdir";
	expressions[C_PRINTENV] = "printenv";
	expressions[C_EXIT] = "exit";
	expressions[C_JOBS] = "jobs";
	expressions[C_NICE] = "nice";
	expressions[C_WAIT] = "wait";

	if(isBuiltInCommand(name.c_str())) {
		builtInCommand = new BuiltInCommand();
	} else {
		builtInCommand = 0;
	}
}

Command::~Command() {
	arguments->clear();
	delete arguments;

	if(builtInCommand != 0) delete builtInCommand;

	pathVariables->erase(pathVariables->begin(), pathVariables->end());
	delete pathVariables;
}

std::string Command::getName() {
	return name;
}

void Command::addArgument(std::string argument) {
	arguments->push_back(argument);
}

std::vector<std::string>* Command::getArguments() {
	return arguments;
}

std::string Command::getIOInput() {
	return ioInput;
}

void Command::setIOInput(std::string ioInput) {
	this->ioInput = ioInput;
}

void Command::setPriorityInc(int inc) {
	this->priorityInc = inc;
}

std::string Command::getIOOutput() {
	return ioOutput;
}

void Command::setIOOutput(std::string ioOutput) {
	this->ioOutput = ioOutput;
}

bool Command::getAppend() {
	return append;
}

void Command::setAppend(bool append) {
	this->append = append;
}

BuiltInCommand* Command::getBuiltInCommmand(){
	return builtInCommand;
}

bool Command::runCommand() {
	return runCommand(true);
}

bool Command::runCommand(bool forked) {
	bool status = true;

	int tempStdIn = -1;
	int tempStdOut = -1;

	// Zet output van file in stdin
	if(getIOInput() != "") {
		int stdInDescriptor;
		if((stdInDescriptor = open(getIOInput().c_str(), O_RDONLY)) < 0) {
			std::cout << getIOInput() << ": No such file or directory" << std::endl;
			tempStdIn = -1;
		} else {
			tempStdIn = dup(0);
			dup2(stdInDescriptor, 0);
			close(stdInDescriptor);
		}
	}

	// Schrijft stdout naar file
	if(getIOOutput() != "" ) {
		int stdOutDescriptor;

		if(getAppend()) {
			stdOutDescriptor = open(getIOOutput().c_str(), O_APPEND | O_WRONLY | O_CREAT, 0666);
		} else {
			stdOutDescriptor = open(getIOOutput().c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0666);
		}

		if (stdOutDescriptor < 0) {
			std::cout << getIOOutput() << ": No such file or directory" << std::endl;
			tempStdOut = -1;
		} else {
			tempStdOut = dup(1);
			dup2(stdOutDescriptor, 1);
			close(stdOutDescriptor);
		}
	}

	if((tempStdIn != -1 || getIOInput() == "") && (tempStdOut != -1 || getIOOutput() == "")) {
		if(isBackground) {
			setPriorityInc(11);
		}

		const char* args[arguments->size() + 2];
		args[0] = getName().c_str();

		if(arguments->size() > 0) {
			for(unsigned int i = 0; i < arguments->size(); i++) {
				args[i + 1] = arguments->at(i).c_str();
			}
		}

		if(isBuiltInCommand(args[0])) {
			executeBuiltInCommand(args[0], arguments->size() + 1, (char* const*) args);
		} else {
			args[arguments->size() + 1] = NULL;
			status = executeCommand(args[0], (char* const*) args, forked);
		}
	}

	if(getIOInput() != "" && tempStdIn != -1) {
		close(0);
		dup2(tempStdIn, 0);
		close(tempStdIn);
	}

	if(getIOOutput() != "" && tempStdOut != -1) {
		close(1);
		dup2(tempStdOut, 1);
		close(tempStdOut);
	}

	return status;
}

bool Command::executeCommand(const char* command, char* const* argv, bool forked) {
	pid_t cpid;
	pid_t errorCheck;
	int status;

	if(forked) {
		cpid = fork();
	} else {
		cpid = 0;
	}

	if (cpid == -1) { //Couldn't make fork
		perror("fork");
		std::cout << "could not create childprocess" << std::endl << std::flush;
	} else if (cpid == 0) { //childprocess
		int niceErr = 0;
		if(priorityInc > 0) niceErr = nice(priorityInc);
		if(niceErr < 0) std::cout << "Adjusted priority failed, executing program anyway" << std::endl << std::flush;

		execv(getCommandPath(command).c_str(), argv);
		std::cout << "program could not be executed, execution terminated" << std::endl << std::flush;
		status = -1;
		return false;
		//exit(EXIT_FAILURE); //TODO: replace with exit(EXIT_SUCCESS); ?
	} else { //parentprocess
		childPid = cpid;
		if(!isBackground) {
			do {
				errorCheck = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
				if (errorCheck == -1) {
					perror("waitpid");
					//exit(EXIT_FAILURE);
				}

				if (WIFEXITED(status)) {
					if(status != 0) {
						printf("exited, status=%d\n", WEXITSTATUS(status));
						return false;
					}
				} else if (WIFSIGNALED(status)) {
					printf("killed by signal %d\n", WTERMSIG(status));
					return false;
				} else if (WIFSTOPPED(status)) {
					printf("stopped by signal %d\n", WSTOPSIG(status));
					return false;
				} else if (WIFCONTINUED(status)) {
					printf("continued\n");
					return false;
				}
			} while (!WIFEXITED(status) && !WIFSIGNALED(status));
			if(status != 0)
				std::cout << "Process: " << childPid << std::endl;
		}
	}

	return true;
}

bool Command::isBuiltInCommand(const char* command) {
	for(int i = 0; i < COMMANDS_SIZE; i++) {
		if(strcmp(expressions[i], command) == 0) { //TODO: vervangen met regexp controle?
			return true;
		}
	}

	//return COMMANDS_SIZE;
	return false;
}

void Command::executeBuiltInCommand(const char* command, int argc, char* const* argv) {
	if(strcmp(command, "exit") == 0) {
		exit(0);
	} else {
		builtInCommand->executeBuildIn(command, argc, argv);
	}
}


std::string Command::getCommandPath(std::string command) {
	std::string s = command;
	for (std::vector<std::string>::iterator iter = pathVariables->begin(); iter != pathVariables->end(); ++iter) {
		std::string commandPath = ((*iter) + "/" + command).c_str();

		if (fileExists(commandPath.c_str()) == 1) {
			s = commandPath;
		}
	}

	return s;
}

bool Command::fileExists(const char* fileName) {
	FILE* fp = NULL;

	fp = fopen(fileName, "rb");
	if(fp != NULL) {
		fclose(fp);

		return true;
	}

	return false;
}

void Command::setBackground(bool setBackground) {
	isBackground = setBackground;
}

int Command::getChildPid() { return childPid; }
